package settings

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"log"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
)

const (
	DefaultGatewayName = "gateway" // 默认网关节点服务名称配置
	DefaultBackenfName = "backend" // 默认后端节点服务名称配置
)

var DefaultSetting = Setting{
	Host:     "localhost",
	BasePath: "rpcx-service",
	ServicePort: map[string]int32{
		DefaultBackenfName: 7890,
		DefaultGatewayName: 8081,
	},

	Registration: "peer2peer",
	RpcxService: map[string][][2]string{
		DefaultBackenfName: {
			{"tcp@localhost:7890", ""},
		},
		DefaultGatewayName: {
			{"tcp@localhost:8081", ""},
		},
	},
}

func Command(cmd *cobra.Command) {
	cmd.AddCommand(func() *cobra.Command {
		initSettingCmd := cobra.Command{Use: "setting", Short: "配置项"}
		initSettingCmd.AddCommand(
			func() *cobra.Command {
				fields := DefaultSetting.parseField()
				cmd := cobra.Command{
					Use:   "init",
					Short: "初始化配置",
					Run: func(cmd *cobra.Command, args []string) {
						var (
							err error
							dir string
						)
						var setting = Setting{}
						if _, err = os.Stat("settings.toml"); !os.IsNotExist(err) {
							if err = setting.Load(); err != nil {
								log.Fatal("settings is exists but read error:", err)
							}
						} else {
							setting = DefaultSetting
						}

						reflectTypeOf := reflect.TypeOf(&setting)
						reflectValueOf := reflect.ValueOf(&setting)
						witherr := func(val interface{}, err error) interface{} {
							if err != nil {
								log.Fatal(err)
							}
							return val
						}
						for i := range fields {
							f := fields[i]
							if f.Dftv == nil {
								continue
							}
							var val interface{}
							switch f.Kind {
							case reflect.Map:
								val = witherr(cmd.Flags().GetStringToString(f.Name))
							case reflect.Slice:
								val = witherr(cmd.Flags().GetStringSlice(f.Name))
							case reflect.String:
								val = witherr(cmd.Flags().GetString(f.Name))
							default:
								val = witherr(cmd.Flags().GetInt64(f.Name))
							}
							method, exists := reflectTypeOf.MethodByName(fmt.Sprintf("%sIntr", f.Name))
							if exists {
								method.Func.Call([]reflect.Value{reflectValueOf, reflect.ValueOf(val)})
							}
						}

						InitTomlFile(dir, setting.parseField())
					},
				}

				for i := range fields {
					f := fields[i]
					if f.Dftv == nil {
						continue
					}
					switch f.Kind {
					case reflect.Map:
						cmd.Flags().StringToString(f.Name, nil, f.Intr)
					case reflect.Slice:
						cmd.Flags().StringSlice(f.Name, nil, f.Intr)
					case reflect.String:
						cmd.Flags().String(f.Name, "", f.Intr)
					default:
						cmd.Flags().Int64(f.Name, 0, f.Intr)
					}
				}

				return &cmd
			}(),
		)
		return &initSettingCmd
	}())
}

type Setting struct {
	Host        string           `json:"host"`
	ServicePort map[string]int32 `json:"service_port"`

	/*-----服务名与访问路径-----*/
	BasePath    string `json:"base_path"`    // 服务注册的根路径
	ServiceName string `json:"service_name"` // 服务节点的名称（需要唯一）

	Registration string `json:"registration" enum:"etcd;redis;peer2peer"`

	/*-------服务治理组件-------*/
	ETCDUrl     string                 `json:"etcd_url"`
	RedisUrl    string                 `json:"redis_url"`
	RpcxService map[string][][2]string `json:"rpcx_service"`

	/*-------自定义配置项-------*/
	GameConfig map[string]string `json:"game_config"`
}

func (s *Setting) HostIntr(host ...string) ([]string, string) {
	var def []string
	if len(host) != 0 {
		if host[0] != "" {
			s.Host = host[0]
		}
	}
	def = append(def, fmt.Sprintf("'%s'", s.Host))
	return def, "当前主机ip 如：Host = xxx.xxx.xxx.xxx"
}

func (s *Setting) ServicePortIntr(port ...map[string]string) ([]string, string) {
	var def []string
	if len(port) != 0 {
		for v, p := range port[0] {
			pp, _ := strconv.Atoi(p)
			if pp != 0 {
				s.ServicePort[v] = int32(pp)
			}
		}
	}
	if s.ServicePort != nil {
		for k, v := range s.ServicePort {
			def = append(def, fmt.Sprintf("%s = %d", k, v))
		}
	}
	return def, `暴露的服务对应的端口如：ServicePort.ws = 8080`
}

func (s *Setting) RedisUrlIntr(url ...string) ([]string, string) {
	var def []string
	if len(url) != 0 {
		if url[0] != "" {
			s.RedisUrl = url[0]
		}
	}
	def = append(def, fmt.Sprintf("'%s'", s.RedisUrl))
	return def, "redis注册中心地址 如：RedisUrl = redis://:password@host:port?db=1"
}

func (s *Setting) ETCDUrlIntr(url ...string) ([]string, string) {
	var def []string
	if len(url) != 0 {
		if url[0] != "" {
			s.ETCDUrl = url[0]
		}
	}
	def = append(def, fmt.Sprintf("'%s'", s.ETCDUrl))
	return def, "etcd注册中心地址 如：ETCDUrl = etcd://username:password@host:port"
}

func (s *Setting) RpcxServiceIntr(rc ...map[string]string) ([]string, string) {
	var def []string
	if len(rc) != 0 {
		for name, value := range rc[0] {
			value = strings.Replace(value, "''", string(byte(0)), -1)   // 处理空字符
			value = strings.Replace(value, "\"\"", string(byte(0)), -1) // 处理空字符

			value = strings.Replace(value, " ", "", -1)  // 去除空格
			value = strings.Replace(value, "[", "", -1)  // 去除正括号
			value = strings.Replace(value, "]", "", -1)  // 去除反括号
			value = strings.Replace(value, "'", "", -1)  // 去除单引号
			value = strings.Replace(value, "\"", "", -1) // 去除双引号
			values := strings.Split(value, ",")
			if len(values)%2 != 0 {
				log.Fatal("RpcxService field pre error!!!")
			}
			s.RpcxService[name] = make([][2]string, len(values)/2)
			for i := 0; i < len(values)/2; i++ {
				s.RpcxService[name][i] = [2]string{values[i*2], values[i*2+1]}
				for x := 0; x < 2; x++ {
					if v := values[i*2+x]; v == string(byte(0)) {
						s.RpcxService[name][i][x] = ""
					} else {
						s.RpcxService[name][i][x] = v
					}
				}
			}
		}
	}

	if len(s.RpcxService) > 0 {
		for svc, addrs := range s.RpcxService {
			temp := fmt.Sprintf("%s = [", svc)
			for idx, addr := range addrs {
				if idx == len(addrs)-1 {
					temp += fmt.Sprintf("\n\t['%s', '%s']\n]", addr[0], addr[1])
				} else {
					temp += fmt.Sprintf("\n\t['%s', '%s'],", addr[0], addr[1])
				}
			}
			if len(addrs) == 0 {
				temp += "]"
			}
			def = append(def, temp)
		}
	}
	return def, "点对点注册中心 rpcx服务端地址 如：RpcxService.backend = [[address1, meta], [address2, meta]]"
}

func (s *Setting) BasePathIntr(p ...string) ([]string, string) {
	var def []string
	if len(p) != 0 {
		if p[0] != "" {
			s.BasePath = p[0]
		}
	}
	def = append(def, fmt.Sprintf("'%s'", s.BasePath))
	return def, "服务注册根路径 如：BasePath = rpcx_test"
}

func (s *Setting) ServiceNameIntr(n ...string) ([]string, string) {
	var def []string
	if len(n) != 0 {
		if n[0] != "" {
			s.ServiceName = n[0]
		}
	}
	def = append(def, fmt.Sprintf("'%s'", s.ServiceName))

	return def, "服务名字 如：ServiceName = agent"
}

func (s *Setting) RegistrationIntr(reg ...string) ([]string, string) {
	var def []string

	if len(reg) != 0 {
		if reg[0] != "" {
			s.Registration = reg[0]
		}
	}

	def = append(def, fmt.Sprintf("'%s'", s.Registration))

	if field, ok := reflect.TypeOf(s).Elem().FieldByName("Registration"); ok {
		return def, fmt.Sprintf(
			"服务治理组件 如：Registration = redis 默认值：%s 可选值：%s",
			field.Tag.Get("default"),
			field.Tag.Get("enum"),
		)
	}

	return def, "服务治理组件 如：Registration = redis"
}

func (s *Setting) GameConfigIntr(conf ...map[string]string) ([]string, string) {
	var def = make([]string, 0)
	if len(conf) != 0 {
		for v, p := range conf[0] {
			s.GameConfig[v] = p
		}
	}
	if s.GameConfig != nil {
		for k, v := range s.GameConfig {
			def = append(def, fmt.Sprintf("%s = '%s'", k, v))
		}
	}
	return def, "自定义配置:类型map[string]string 读取:setting.GameConfig[xxxx]"
}

type FieldInfo struct {
	Name string
	Intr string
	Dftv []string
	Kind reflect.Kind
}

func (f *FieldInfo) PreFix(str string) string {
	return strings.Replace(strings.Split(str, "=")[0], " ", "", -1)
}

func (f *FieldInfo) PreVal(str string) string {
	return strings.Replace(strings.Split(str, "=")[1], " ", "", -1)
}

func (s *Setting) parseField() (fields []*FieldInfo) {
	reflectTypeOf := reflect.TypeOf(s)
	reflectValueOf := reflect.ValueOf(s)
	for i := 0; i < reflectTypeOf.Elem().NumField(); i++ {
		field := new(FieldInfo)
		fields = append(fields, field)

		fieldreft := reflectTypeOf.Elem().Field(i)
		fieldrefv := reflectValueOf.Elem().Field(i)

		field.Name = fieldreft.Name
		field.Kind = fieldrefv.Type().Kind()

		method, exists := reflectTypeOf.MethodByName(fmt.Sprintf("%sIntr", fieldreft.Name))
		if exists {
			res := method.Func.Call([]reflect.Value{reflectValueOf})

			field.Intr = res[1].String()
			field.Dftv = res[0].Interface().([]string)
		}
	}
	return
}

func (s *Setting) Load() error {
	v := viper.New()
	v.AddConfigPath("./")
	v.SetConfigType("toml")
	v.SetConfigName("settings.toml")

	v.AutomaticEnv()
	v.SetEnvPrefix("GM")
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))

	if err := v.ReadInConfig(); err != nil {
		return err
	} else {
		if err := v.Unmarshal(s); err != nil {
			return err
		}
	}
	return nil
}

func InitTomlFile(dir string, fields []*FieldInfo) {
	settingfile := "# default file settings\n\n"

	for _, field := range fields {
		settingfile += fmt.Sprintf("# %s \n", field.Intr)
		switch field.Kind {
		case reflect.Map:
			if len(field.Dftv) == 0 {
				settingfile += fmt.Sprintf("# %s\n", field.Name)
			} else {
				for _, def := range field.Dftv {
					settingfile += fmt.Sprintf("%s.%s\n", field.Name, def)
				}
			}
		case reflect.Slice:
			if len(field.Dftv) == 0 {
				settingfile += fmt.Sprintf("# %s\n", field.Name)
			} else {
				for _, def := range field.Dftv {
					settingfile += fmt.Sprintf("%s = %s\n", field.Name, def)
				}
			}
		case reflect.String:
			if len(field.Dftv) == 0 {
				settingfile += fmt.Sprintf("%s = ''\n", field.Name)
			} else {
				settingfile += fmt.Sprintf("%s = %s\n", field.Name, field.Dftv[0])
			}
		default: // 其他类型 默认为 数字类型
			if len(field.Dftv) == 0 {
				settingfile += fmt.Sprintf("%s = 0\n", field.Name)
			} else {
				settingfile += fmt.Sprintf("%s = %s\n", field.Name, field.Dftv[0])
			}
		}

		settingfile += "\n"
	}

	filePath := path.Join(dir, "settings.toml")
	if f, e := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0766); e != nil {
		log.Fatal("OpenFile ./settings.toml ERROR:", e)
	} else {
		if _, e = f.Write([]byte(settingfile)); e != nil {
			log.Fatal("settings InitTomlFile ERROR:", e)
		} else {
			if e = f.Close(); e != nil {
				log.Fatal("close ./settings.toml ERROR:", e)
			}
		}
	}
}
